home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / lib / ruby / 1.8 / debug.rb < prev    next >
Text File  |  2007-02-12  |  21KB  |  948 lines

  1. # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
  2. # Copyright (C) 2000  Information-technology Promotion Agency, Japan
  3. # Copyright (C) 2000-2003  NAKAMURA, Hiroshi  <nahi@ruby-lang.org>
  4.  
  5. if $SAFE > 0
  6.   STDERR.print "-r debug.rb is not available in safe mode\n"
  7.   exit 1
  8. end
  9.  
  10. require 'tracer'
  11. require 'pp'
  12.  
  13. class Tracer
  14.   def Tracer.trace_func(*vars)
  15.     Single.trace_func(*vars)
  16.   end
  17. end
  18.  
  19. SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
  20.  
  21. class DEBUGGER__
  22. class Mutex
  23.   def initialize
  24.     @locker = nil
  25.     @waiting = []
  26.     @locked = false;
  27.   end
  28.  
  29.   def locked?
  30.     @locked
  31.   end
  32.  
  33.   def lock
  34.     return if Thread.critical
  35.     return if @locker == Thread.current
  36.     while (Thread.critical = true; @locked)
  37.       @waiting.push Thread.current
  38.       Thread.stop
  39.     end
  40.     @locked = true
  41.     @locker = Thread.current
  42.     Thread.critical = false
  43.     self
  44.   end
  45.  
  46.   def unlock
  47.     return if Thread.critical
  48.     return unless @locked
  49.     unless @locker == Thread.current
  50.       raise RuntimeError, "unlocked by other"
  51.     end
  52.     Thread.critical = true
  53.     t = @waiting.shift
  54.     @locked = false
  55.     @locker = nil
  56.     Thread.critical = false
  57.     t.run if t
  58.     self
  59.   end
  60. end
  61. MUTEX = Mutex.new
  62.  
  63. class Context
  64.   DEBUG_LAST_CMD = []
  65.  
  66.   begin
  67.     require 'readline'
  68.     def readline(prompt, hist)
  69.       Readline::readline(prompt, hist)
  70.     end
  71.   rescue LoadError
  72.     def readline(prompt, hist)
  73.       STDOUT.print prompt
  74.       STDOUT.flush
  75.       line = STDIN.gets
  76.       exit unless line
  77.       line.chomp!
  78.       line
  79.     end
  80.     USE_READLINE = false
  81.   end
  82.  
  83.   def initialize
  84.     if Thread.current == Thread.main
  85.       @stop_next = 1
  86.     else
  87.       @stop_next = 0
  88.     end
  89.     @last_file = nil
  90.     @file = nil
  91.     @line = nil
  92.     @no_step = nil
  93.     @frames = []
  94.     @finish_pos = 0
  95.     @trace = false
  96.     @catch = "StandardError"
  97.     @suspend_next = false
  98.   end
  99.  
  100.   def stop_next(n=1)
  101.     @stop_next = n
  102.   end
  103.  
  104.   def set_suspend
  105.     @suspend_next = true
  106.   end
  107.  
  108.   def clear_suspend
  109.     @suspend_next = false
  110.   end
  111.  
  112.   def suspend_all
  113.     DEBUGGER__.suspend
  114.   end
  115.  
  116.   def resume_all
  117.     DEBUGGER__.resume
  118.   end
  119.  
  120.   def check_suspend
  121.     return if Thread.critical
  122.     while (Thread.critical = true; @suspend_next)
  123.       DEBUGGER__.waiting.push Thread.current
  124.       @suspend_next = false
  125.       Thread.stop
  126.     end
  127.     Thread.critical = false
  128.   end
  129.  
  130.   def trace?
  131.     @trace
  132.   end
  133.  
  134.   def set_trace(arg)
  135.     @trace = arg
  136.   end
  137.  
  138.   def stdout
  139.     DEBUGGER__.stdout
  140.   end
  141.  
  142.   def break_points
  143.     DEBUGGER__.break_points
  144.   end
  145.  
  146.   def display
  147.     DEBUGGER__.display
  148.   end
  149.  
  150.   def context(th)
  151.     DEBUGGER__.context(th)
  152.   end
  153.  
  154.   def set_trace_all(arg)
  155.     DEBUGGER__.set_trace(arg)
  156.   end
  157.  
  158.   def set_last_thread(th)
  159.     DEBUGGER__.set_last_thread(th)
  160.   end
  161.  
  162.   def debug_eval(str, binding)
  163.     begin
  164.       val = eval(str, binding)
  165.     rescue StandardError, ScriptError => e
  166.       at = eval("caller(1)", binding)
  167.       stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
  168.       for i in at
  169.     stdout.printf "\tfrom %s\n", i
  170.       end
  171.       throw :debug_error
  172.     end
  173.   end
  174.  
  175.   def debug_silent_eval(str, binding)
  176.     begin
  177.       eval(str, binding)
  178.     rescue StandardError, ScriptError
  179.       nil
  180.     end
  181.   end
  182.  
  183.   def var_list(ary, binding)
  184.     ary.sort!
  185.     for v in ary
  186.       stdout.printf "  %s => %s\n", v, eval(v, binding).inspect
  187.     end
  188.   end
  189.  
  190.   def debug_variable_info(input, binding)
  191.     case input
  192.     when /^\s*g(?:lobal)?\s*$/
  193.       var_list(global_variables, binding)
  194.  
  195.     when /^\s*l(?:ocal)?\s*$/
  196.       var_list(eval("local_variables", binding), binding)
  197.  
  198.     when /^\s*i(?:nstance)?\s+/
  199.       obj = debug_eval($', binding)
  200.       var_list(obj.instance_variables, obj.instance_eval{binding()})
  201.  
  202.     when /^\s*c(?:onst(?:ant)?)?\s+/
  203.       obj = debug_eval($', binding)
  204.       unless obj.kind_of? Module
  205.     stdout.print "Should be Class/Module: ", $', "\n"
  206.       else
  207.     var_list(obj.constants, obj.module_eval{binding()})
  208.       end
  209.     end
  210.   end
  211.  
  212.   def debug_method_info(input, binding)
  213.     case input
  214.     when /^i(:?nstance)?\s+/
  215.       obj = debug_eval($', binding)
  216.  
  217.       len = 0
  218.       for v in obj.methods.sort
  219.     len += v.size + 1
  220.     if len > 70
  221.       len = v.size + 1
  222.       stdout.print "\n"
  223.     end
  224.     stdout.print v, " "
  225.       end
  226.       stdout.print "\n"
  227.  
  228.     else
  229.       obj = debug_eval(input, binding)
  230.       unless obj.kind_of? Module
  231.     stdout.print "Should be Class/Module: ", input, "\n"
  232.       else
  233.     len = 0
  234.     for v in obj.instance_methods(false).sort
  235.       len += v.size + 1
  236.       if len > 70
  237.         len = v.size + 1
  238.         stdout.print "\n"
  239.       end
  240.       stdout.print v, " "
  241.     end
  242.     stdout.print "\n"
  243.       end
  244.     end
  245.   end
  246.  
  247.   def thnum
  248.     num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
  249.     unless num
  250.       DEBUGGER__.make_thread_list
  251.       num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
  252.     end
  253.     num
  254.   end
  255.  
  256.   def debug_command(file, line, id, binding)
  257.     MUTEX.lock
  258.     unless defined?($debugger_restart) and $debugger_restart
  259.       callcc{|c| $debugger_restart = c} 
  260.     end
  261.     set_last_thread(Thread.current)
  262.     frame_pos = 0
  263.     binding_file = file
  264.     binding_line = line
  265.     previous_line = nil
  266.     if ENV['EMACS']
  267.       stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
  268.     else
  269.       stdout.printf "%s:%d:%s", binding_file, binding_line,
  270.     line_at(binding_file, binding_line)
  271.     end
  272.     @frames[0] = [binding, file, line, id]
  273.     display_expressions(binding)
  274.     prompt = true
  275.     while prompt and input = readline("(rdb:%d) "%thnum(), true)
  276.       catch(:debug_error) do
  277.     if input == ""
  278.           next unless DEBUG_LAST_CMD[0]
  279.       input = DEBUG_LAST_CMD[0]
  280.       stdout.print input, "\n"
  281.     else
  282.       DEBUG_LAST_CMD[0] = input
  283.     end
  284.  
  285.     case input
  286.     when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
  287.           if defined?( $2 )
  288.             if $1 == 'on'
  289.               set_trace_all true
  290.             else
  291.               set_trace_all false
  292.             end
  293.           elsif defined?( $1 )
  294.             if $1 == 'on'
  295.               set_trace true
  296.             else
  297.               set_trace false
  298.             end
  299.           end
  300.           if trace?
  301.             stdout.print "Trace on.\n"
  302.           else
  303.             stdout.print "Trace off.\n"
  304.           end
  305.  
  306.     when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
  307.       pos = $2
  308.           if $1
  309.             klass = debug_silent_eval($1, binding)
  310.             file = $1
  311.           end
  312.       if pos =~ /^\d+$/
  313.         pname = pos
  314.         pos = pos.to_i
  315.       else
  316.         pname = pos = pos.intern.id2name
  317.       end
  318.       break_points.push [true, 0, klass || file, pos]
  319.       stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
  320.  
  321.     when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
  322.       pos = $2.intern.id2name
  323.       klass = debug_eval($1, binding)
  324.       break_points.push [true, 0, klass, pos]
  325.       stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
  326.  
  327.     when /^\s*wat(?:ch)?\s+(.+)$/
  328.       exp = $1
  329.       break_points.push [true, 1, exp]
  330.       stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
  331.  
  332.     when /^\s*b(?:reak)?$/
  333.       if break_points.find{|b| b[1] == 0}
  334.         n = 1
  335.         stdout.print "Breakpoints:\n"
  336.         for b in break_points
  337.           if b[0] and b[1] == 0
  338.         stdout.printf "  %d %s:%s\n", n, b[2], b[3] 
  339.           end
  340.           n += 1
  341.         end
  342.       end
  343.       if break_points.find{|b| b[1] == 1}
  344.         n = 1
  345.         stdout.print "\n"
  346.         stdout.print "Watchpoints:\n"
  347.         for b in break_points
  348.           if b[0] and b[1] == 1
  349.         stdout.printf "  %d %s\n", n, b[2]
  350.           end
  351.           n += 1
  352.         end
  353.       end
  354.       if break_points.size == 0
  355.         stdout.print "No breakpoints\n"
  356.       else
  357.         stdout.print "\n"
  358.       end
  359.  
  360.     when /^\s*del(?:ete)?(?:\s+(\d+))?$/
  361.       pos = $1
  362.       unless pos
  363.         input = readline("Clear all breakpoints? (y/n) ", false)
  364.         if input == "y"
  365.           for b in break_points
  366.         b[0] = false
  367.           end
  368.         end
  369.       else
  370.         pos = pos.to_i
  371.         if break_points[pos-1]
  372.           break_points[pos-1][0] = false
  373.         else
  374.           stdout.printf "Breakpoint %d is not defined\n", pos
  375.         end
  376.       end
  377.  
  378.     when /^\s*disp(?:lay)?\s+(.+)$/
  379.       exp = $1
  380.       display.push [true, exp]
  381.       stdout.printf "%d: ", display.size
  382.       display_expression(exp, binding)
  383.  
  384.     when /^\s*disp(?:lay)?$/
  385.       display_expressions(binding)
  386.  
  387.     when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
  388.       pos = $1
  389.       unless pos
  390.         input = readline("Clear all expressions? (y/n) ", false)
  391.         if input == "y"
  392.           for d in display
  393.         d[0] = false
  394.           end
  395.         end
  396.       else
  397.         pos = pos.to_i
  398.         if display[pos-1]
  399.           display[pos-1][0] = false
  400.         else
  401.           stdout.printf "Display expression %d is not defined\n", pos
  402.         end
  403.       end
  404.  
  405.     when /^\s*c(?:ont)?$/
  406.       prompt = false
  407.  
  408.     when /^\s*s(?:tep)?(?:\s+(\d+))?$/
  409.       if $1
  410.         lev = $1.to_i
  411.       else
  412.         lev = 1
  413.       end
  414.       @stop_next = lev
  415.       prompt = false
  416.  
  417.     when /^\s*n(?:ext)?(?:\s+(\d+))?$/
  418.       if $1
  419.         lev = $1.to_i
  420.       else
  421.         lev = 1
  422.       end
  423.       @stop_next = lev
  424.       @no_step = @frames.size - frame_pos
  425.       prompt = false
  426.  
  427.     when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
  428.       display_frames(frame_pos)
  429.  
  430.     when /^\s*l(?:ist)?(?:\s+(.+))?$/
  431.       if not $1
  432.         b = previous_line ? previous_line + 10 : binding_line - 5
  433.         e = b + 9
  434.       elsif $1 == '-'
  435.         b = previous_line ? previous_line - 10 : binding_line - 5
  436.         e = b + 9
  437.       else
  438.         b, e = $1.split(/[-,]/)
  439.         if e
  440.           b = b.to_i
  441.           e = e.to_i
  442.         else
  443.           b = b.to_i - 5
  444.           e = b + 9
  445.         end
  446.       end
  447.       previous_line = b
  448.       display_list(b, e, binding_file, binding_line)
  449.  
  450.     when /^\s*up(?:\s+(\d+))?$/
  451.       previous_line = nil
  452.       if $1
  453.         lev = $1.to_i
  454.       else
  455.         lev = 1
  456.       end
  457.       frame_pos += lev
  458.       if frame_pos >= @frames.size
  459.         frame_pos = @frames.size - 1
  460.         stdout.print "At toplevel\n"
  461.       end
  462.       binding, binding_file, binding_line = @frames[frame_pos]
  463.       stdout.print format_frame(frame_pos)
  464.  
  465.     when /^\s*down(?:\s+(\d+))?$/
  466.       previous_line = nil
  467.       if $1
  468.         lev = $1.to_i
  469.       else
  470.         lev = 1
  471.       end
  472.       frame_pos -= lev
  473.       if frame_pos < 0
  474.         frame_pos = 0
  475.         stdout.print "At stack bottom\n"
  476.       end
  477.       binding, binding_file, binding_line = @frames[frame_pos]
  478.       stdout.print format_frame(frame_pos)
  479.  
  480.     when /^\s*fin(?:ish)?$/
  481.       if frame_pos == @frames.size
  482.         stdout.print "\"finish\" not meaningful in the outermost frame.\n"
  483.       else
  484.         @finish_pos = @frames.size - frame_pos
  485.         frame_pos = 0
  486.         prompt = false
  487.       end
  488.  
  489.     when /^\s*cat(?:ch)?(?:\s+(.+))?$/
  490.       if $1
  491.         excn = $1
  492.         if excn == 'off'
  493.           @catch = nil
  494.           stdout.print "Clear catchpoint.\n"
  495.         else
  496.           @catch = excn
  497.           stdout.printf "Set catchpoint %s.\n", @catch
  498.         end
  499.       else
  500.         if @catch
  501.           stdout.printf "Catchpoint %s.\n", @catch
  502.         else
  503.           stdout.print "No catchpoint.\n"
  504.         end
  505.       end
  506.  
  507.     when /^\s*q(?:uit)?$/
  508.       input = readline("Really quit? (y/n) ", false)
  509.       if input == "y"
  510.         exit!    # exit -> exit!: No graceful way to stop threads...
  511.       end
  512.  
  513.     when /^\s*v(?:ar)?\s+/
  514.       debug_variable_info($', binding)
  515.  
  516.     when /^\s*m(?:ethod)?\s+/
  517.       debug_method_info($', binding)
  518.  
  519.     when /^\s*th(?:read)?\s+/
  520.       if DEBUGGER__.debug_thread_info($', binding) == :cont
  521.         prompt = false
  522.       end
  523.  
  524.     when /^\s*pp\s+/
  525.       PP.pp(debug_eval($', binding), stdout)
  526.  
  527.     when /^\s*p\s+/
  528.       stdout.printf "%s\n", debug_eval($', binding).inspect
  529.  
  530.     when /^\s*r(?:estart)?$/
  531.           $debugger_restart.call
  532.  
  533.     when /^\s*h(?:elp)?$/
  534.       debug_print_help()
  535.  
  536.     else
  537.       v = debug_eval(input, binding)
  538.       stdout.printf "%s\n", v.inspect
  539.     end
  540.       end
  541.     end
  542.     MUTEX.unlock
  543.     resume_all
  544.   end
  545.  
  546.   def debug_print_help
  547.     stdout.print <<EOHELP
  548. Debugger help v.-0.002b
  549. Commands
  550.   b[reak] [file:|class:]<line|method>
  551.   b[reak] [class.]<line|method>
  552.                              set breakpoint to some position
  553.   wat[ch] <expression>       set watchpoint to some expression
  554.   cat[ch] (<exception>|off)  set catchpoint to an exception
  555.   b[reak]                    list breakpoints
  556.   cat[ch]                    show catchpoint
  557.   del[ete][ nnn]             delete some or all breakpoints
  558.   disp[lay] <expression>     add expression into display expression list
  559.   undisp[lay][ nnn]          delete one particular or all display expressions
  560.   c[ont]                     run until program ends or hit breakpoint
  561.   s[tep][ nnn]               step (into methods) one line or till line nnn
  562.   n[ext][ nnn]               go over one line or till line nnn
  563.   w[here]                    display frames
  564.   f[rame]                    alias for where
  565.   l[ist][ (-|nn-mm)]         list program, - lists backwards
  566.                              nn-mm lists given lines
  567.   up[ nn]                    move to higher frame
  568.   down[ nn]                  move to lower frame
  569.   fin[ish]                   return to outer frame
  570.   tr[ace] (on|off)           set trace mode of current thread
  571.   tr[ace] (on|off) all       set trace mode of all threads
  572.   q[uit]                     exit from debugger
  573.   v[ar] g[lobal]             show global variables
  574.   v[ar] l[ocal]              show local variables
  575.   v[ar] i[nstance] <object>  show instance variables of object
  576.   v[ar] c[onst] <object>     show constants of object
  577.   m[ethod] i[nstance] <obj>  show methods of object
  578.   m[ethod] <class|module>    show instance methods of class or module
  579.   th[read] l[ist]            list all threads
  580.   th[read] c[ur[rent]]       show current thread
  581.   th[read] [sw[itch]] <nnn>  switch thread context to nnn
  582.   th[read] stop <nnn>        stop thread nnn
  583.   th[read] resume <nnn>      resume thread nnn
  584.   p expression               evaluate expression and print its value
  585.   h[elp]                     print this help
  586.   <everything else>          evaluate
  587. EOHELP
  588.   end
  589.  
  590.   def display_expressions(binding)
  591.     n = 1
  592.     for d in display
  593.       if d[0]
  594.     stdout.printf "%d: ", n
  595.     display_expression(d[1], binding)
  596.       end
  597.       n += 1
  598.     end
  599.   end
  600.  
  601.   def display_expression(exp, binding)
  602.     stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
  603.   end
  604.  
  605.   def frame_set_pos(file, line)
  606.     if @frames[0]
  607.       @frames[0][1] = file
  608.       @frames[0][2] = line
  609.     end
  610.   end
  611.  
  612.   def display_frames(pos)
  613.     0.upto(@frames.size - 1) do |n|
  614.       if n == pos
  615.     stdout.print "--> "
  616.       else
  617.     stdout.print "    "
  618.       end
  619.       stdout.print format_frame(n)
  620.     end
  621.   end
  622.  
  623.   def format_frame(pos)
  624.     bind, file, line, id = @frames[pos]
  625.     sprintf "#%d %s:%s%s\n", pos + 1, file, line,
  626.       (id ? ":in `#{id.id2name}'" : "")
  627.   end
  628.  
  629.   def display_list(b, e, file, line)
  630.     stdout.printf "[%d, %d] in %s\n", b, e, file
  631.     if lines = SCRIPT_LINES__[file] and lines != true
  632.       n = 0
  633.       b.upto(e) do |n|
  634.     if n > 0 && lines[n-1]
  635.       if n == line
  636.         stdout.printf "=> %d  %s\n", n, lines[n-1].chomp
  637.       else
  638.         stdout.printf "   %d  %s\n", n, lines[n-1].chomp
  639.       end
  640.     end
  641.       end
  642.     else
  643.       stdout.printf "No sourcefile available for %s\n", file
  644.     end
  645.   end
  646.  
  647.   def line_at(file, line)
  648.     lines = SCRIPT_LINES__[file]
  649.     if lines
  650.       return "\n" if lines == true
  651.       line = lines[line-1]
  652.       return "\n" unless line
  653.       return line
  654.     end
  655.     return "\n"
  656.   end
  657.  
  658.   def debug_funcname(id)
  659.     if id.nil?
  660.       "toplevel"
  661.     else
  662.       id.id2name
  663.     end
  664.   end
  665.  
  666.   def check_break_points(file, klass, pos, binding, id)
  667.     return false if break_points.empty?
  668.     n = 1
  669.     for b in break_points
  670.       if b[0]        # valid
  671.     if b[1] == 0    # breakpoint
  672.       if (b[2] == file and b[3] == pos) or
  673.           (klass and b[2] == klass and b[3] == pos)
  674.         stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
  675.         return true
  676.       end
  677.     elsif b[1] == 1    # watchpoint
  678.       if debug_silent_eval(b[2], binding)
  679.         stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
  680.         return true
  681.       end
  682.     end
  683.       end
  684.       n += 1
  685.     end
  686.     return false
  687.   end
  688.  
  689.   def excn_handle(file, line, id, binding)
  690.     if $!.class <= SystemExit
  691.       set_trace_func nil
  692.       exit
  693.     end
  694.  
  695.     if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
  696.       stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
  697.       fs = @frames.size
  698.       tb = caller(0)[-fs..-1]
  699.       if tb
  700.     for i in tb
  701.       stdout.printf "\tfrom %s\n", i
  702.     end
  703.       end
  704.       suspend_all
  705.       debug_command(file, line, id, binding)
  706.     end
  707.   end
  708.  
  709.   def trace_func(event, file, line, id, binding, klass)
  710.     Tracer.trace_func(event, file, line, id, binding, klass) if trace?
  711.     context(Thread.current).check_suspend
  712.     @file = file
  713.     @line = line
  714.     case event
  715.     when 'line'
  716.       frame_set_pos(file, line)
  717.       if !@no_step or @frames.size == @no_step
  718.     @stop_next -= 1
  719.     @stop_next = -1 if @stop_next < 0
  720.       elsif @frames.size < @no_step
  721.     @stop_next = 0        # break here before leaving...
  722.       else
  723.     # nothing to do. skipped.
  724.       end
  725.       if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
  726.     @no_step = nil
  727.     suspend_all
  728.     debug_command(file, line, id, binding)
  729.       end
  730.  
  731.     when 'call'
  732.       @frames.unshift [binding, file, line, id]
  733.       if check_break_points(file, klass, id.id2name, binding, id)
  734.     suspend_all
  735.     debug_command(file, line, id, binding)
  736.       end
  737.  
  738.     when 'c-call'
  739.       frame_set_pos(file, line)
  740.  
  741.     when 'class'
  742.       @frames.unshift [binding, file, line, id]
  743.  
  744.     when 'return', 'end'
  745.       if @frames.size == @finish_pos
  746.     @stop_next = 1
  747.     @finish_pos = 0
  748.       end
  749.       @frames.shift
  750.  
  751.     when 'end'
  752.       @frames.shift
  753.  
  754.     when 'raise' 
  755.       excn_handle(file, line, id, binding)
  756.  
  757.     end
  758.     @last_file = file
  759.   end
  760. end
  761.  
  762. trap("INT") { DEBUGGER__.interrupt }
  763. @last_thread = Thread::main
  764. @max_thread = 1
  765. @thread_list = {Thread::main => 1}
  766. @break_points = []
  767. @display = []
  768. @waiting = []
  769. @stdout = STDOUT
  770.  
  771. class << DEBUGGER__
  772.   def stdout
  773.     @stdout
  774.   end
  775.  
  776.   def stdout=(s)
  777.     @stdout = s
  778.   end
  779.  
  780.   def display
  781.     @display
  782.   end
  783.  
  784.   def break_points
  785.     @break_points
  786.   end
  787.  
  788.   def waiting
  789.     @waiting
  790.   end
  791.  
  792.   def set_trace( arg )
  793.     saved_crit = Thread.critical
  794.     Thread.critical = true
  795.     make_thread_list
  796.     for th, in @thread_list
  797.       context(th).set_trace arg
  798.     end
  799.     Thread.critical = saved_crit
  800.     arg
  801.   end
  802.  
  803.   def set_last_thread(th)
  804.     @last_thread = th
  805.   end
  806.  
  807.   def suspend
  808.     saved_crit = Thread.critical
  809.     Thread.critical = true
  810.     make_thread_list
  811.     for th, in @thread_list
  812.       next if th == Thread.current
  813.       context(th).set_suspend
  814.     end
  815.     Thread.critical = saved_crit
  816.     # Schedule other threads to suspend as soon as possible.
  817.     Thread.pass unless Thread.critical
  818.   end
  819.  
  820.   def resume
  821.     saved_crit = Thread.critical
  822.     Thread.critical = true
  823.     make_thread_list
  824.     for th, in @thread_list
  825.       next if th == Thread.current
  826.       context(th).clear_suspend
  827.     end
  828.     waiting.each do |th|
  829.       th.run
  830.     end
  831.     waiting.clear
  832.     Thread.critical = saved_crit
  833.     # Schedule other threads to restart as soon as possible.
  834.     Thread.pass
  835.   end
  836.  
  837.   def context(thread=Thread.current)
  838.     c = thread[:__debugger_data__]
  839.     unless c
  840.       thread[:__debugger_data__] = c = Context.new
  841.     end
  842.     c
  843.   end
  844.  
  845.   def interrupt
  846.     context(@last_thread).stop_next
  847.   end
  848.  
  849.   def get_thread(num)
  850.     th = @thread_list.index(num)
  851.     unless th
  852.       @stdout.print "No thread ##{num}\n"
  853.       throw :debug_error
  854.     end
  855.     th
  856.   end
  857.  
  858.   def thread_list(num)
  859.     th = get_thread(num)
  860.     if th == Thread.current
  861.       @stdout.print "+"
  862.     else
  863.       @stdout.print " "
  864.     end
  865.     @stdout.printf "%d ", num
  866.     @stdout.print th.inspect, "\t"
  867.     file = context(th).instance_eval{@file}
  868.     if file
  869.       @stdout.print file,":",context(th).instance_eval{@line}
  870.     end
  871.     @stdout.print "\n"
  872.   end
  873.  
  874.   def thread_list_all
  875.     for th in @thread_list.values.sort
  876.       thread_list(th)
  877.     end
  878.   end
  879.  
  880.   def make_thread_list
  881.     hash = {}
  882.     for th in Thread::list
  883.       if @thread_list.key? th
  884.     hash[th] = @thread_list[th]
  885.       else
  886.     @max_thread += 1
  887.     hash[th] = @max_thread
  888.       end
  889.     end
  890.     @thread_list = hash
  891.   end
  892.  
  893.   def debug_thread_info(input, binding)
  894.     case input
  895.     when /^l(?:ist)?/
  896.       make_thread_list
  897.       thread_list_all
  898.  
  899.     when /^c(?:ur(?:rent)?)?$/
  900.       make_thread_list
  901.       thread_list(@thread_list[Thread.current])
  902.  
  903.     when /^(?:sw(?:itch)?\s+)?(\d+)/
  904.       make_thread_list
  905.       th = get_thread($1.to_i)
  906.       if th == Thread.current
  907.     @stdout.print "It's the current thread.\n"
  908.       else
  909.     thread_list(@thread_list[th])
  910.     context(th).stop_next
  911.     th.run
  912.     return :cont
  913.       end
  914.  
  915.     when /^stop\s+(\d+)/
  916.       make_thread_list
  917.       th = get_thread($1.to_i)
  918.       if th == Thread.current
  919.     @stdout.print "It's the current thread.\n"
  920.       elsif th.stop?
  921.     @stdout.print "Already stopped.\n"
  922.       else
  923.     thread_list(@thread_list[th])
  924.     context(th).suspend 
  925.       end
  926.  
  927.     when /^resume\s+(\d+)/
  928.       make_thread_list
  929.       th = get_thread($1.to_i)
  930.       if th == Thread.current
  931.     @stdout.print "It's the current thread.\n"
  932.       elsif !th.stop?
  933.     @stdout.print "Already running."
  934.       else
  935.     thread_list(@thread_list[th])
  936.     th.run
  937.       end
  938.     end
  939.   end
  940. end
  941.  
  942. stdout.printf "Debug.rb\n"
  943. stdout.printf "Emacs support available.\n\n"
  944. set_trace_func proc { |event, file, line, id, binding, klass, *rest|
  945.   DEBUGGER__.context.trace_func event, file, line, id, binding, klass
  946. }
  947. end
  948.